/**
 * Created by woziji00226 on 2024-10-18 09:37:53.
 */

let WinWidth;
let WinHeight;
let roadWidth;
let roadSegLength;
let roadCount;//3.14*600
let itemSize;
let charItem;
let score;
let itemTexs;

//道路
class Road {
    //x坐标
    public x: number;
    //y坐标
    public y: number;
    //z坐标
    public z: number;

    //缩放率
    public scale: number;
    //曲率
    public curve: number;

    //投影在屏幕上的X坐标
    public X: number;
    //投影在屏幕上的Y坐标
    public Y: number;
    //按照投影缩放的width
    public W: number;

    //运算符
    public operatorIndex: number;
    //数字
    public numberIndex: number;

    constructor(x: number, y: number, z: number, curve: number) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.curve = curve;
        this.generateItem(false);
    }


    //计算一个随机数
    private rand() {
        return Math.floor(Math.random() * 32767);
    }

    /**
     * 生成 item 就是那些数字和运算符
     * @param bAlwaysGen 
     */
    public generateItem(bAlwaysGen: boolean): void {
        if (bAlwaysGen || this.rand() % 200 == 0) {
            //根据 charItem 运算符 在字符串 索引10以后，所以，随机数就随机0-4 加上10，即随机在10-14
            this.operatorIndex = (this.rand() % 5) + 10;
            //同理 数字索引不超过10 即随机 0-9
            this.numberIndex = this.rand() % 10;
            if (this.numberIndex == 9) {
                this.numberIndex = 0;
            }
        }
        else {
            this.operatorIndex = -1;
        }
    }


    /**
     * 透视投影 得到 屏幕上的位置和宽度。
     * 
     * 3d坐标系为
     * 
     *    
     * Y  Z
     * | /
     * |/
     * .----X
     * (0,0)
     * 
     * y垂直于 Z X平面。相机面向Z 不可以旋转
     * 
     * 
     * 2d坐标系为
     * 
     * (0,0)
     * .----X
     * |
     * |
     * Y
     * 
     * @param camX 
     * @param camY 
     * @param camZ 
     */
    project(camX: number, camY: number, camZ: number) {
        //根据近大远小的原理 离屏幕越远的位置，就应该最小，所以这里用一个反比例函数
        //z越大 scale就越接近0，z和camZ越接近，scale就趋于1
        this.scale = 1.0 / (this.z - camZ);
        //计算X Y 经过近大远小 投影到屏幕。 分成两部分
        /*
        this.X = (1 + (this.x - camX) * this.scale) * WinWidth / 2;
        this.Y = (1 - (this.y - camY) * this.scale) * WinHeight / 2;
        */
        //根据 将该road的x转换到相机坐标系内。只有平移。
        this.X = this.x - camX;
        //根据近大远小，对road的x进行缩放，即离得越近x越大，离得越远，x越小。
        this.X = this.X * this.scale;
        //经过缩放后的X已经很小了。范围大概在[-1,1]之中。然后将其映射到 [0,1]之中，再乘以屏幕宽度，得到road真实在屏幕上的X坐标
        this.X = ((1 + this.X) / 2) * WinWidth;

        //Y同理 离得越远，Y越小 因为2维坐标系 Y向下是正方向
        this.Y = (1 - (this.y - camY) * this.scale) * WinHeight / 2;

        //最后再根据缩放计算道路宽度就行了
        this.W = this.scale * roadWidth * WinWidth / 2;
    }


    /**
     * 绘制物品 （文字）
     * @param g 
     * @param index 
     * @param xPlacement 
     */
    private _drawItem(g: Graphics, index: number, xPlacement: number) {
        //找到 要截取字符图片的位置
        //绘制图片的宽度与道路宽度相同 this.W / itemSize
        if (itemTexs[index] != null) {
            if (this.Y >= Config.WINDOW_HEIGHT) {
                //超出屏幕就不绘制了 要不然会有bug
                return;
            }
            g.drawTexture(itemTexs[index], this.X + xPlacement * this.W, this.Y - this.W, this.W, this.W);
        }
    }


    /**
     * 绘制物品（文字）
     * @param g 
     */
    drawItem(g: Graphics) {
        if (this.operatorIndex == -1) {
            return;
        }
        this._drawItem(g, this.operatorIndex, -1);
        this._drawItem(g, this.numberIndex, 0);

    }

}


/**
 * 绘制梯形
 * 
 *          (x1,y1)
 *   /——————.——————\
 *  /               \
 * /————————.————————\
 *          (x2,y2)
 * 
 * 绘制思路，绘制多边形，
 * 从左上角开始 x1-w1,y1
 * 然后到左下角 x2-w2,y2
 * ....
 * 
 * @param color 颜色
 * @param x1 梯形上边中心点x
 * @param y1 梯形上边中心点y
 * @param w1 梯形上边宽度/2
 * @param x2 梯形下边中心点x
 * @param y2 梯形下边中心点y
 * @param w2 梯形下边宽度/2
 */
function DrawTrape(g: Graphics, color: string, x1: number, y1: number, w1: number, x2: number, y2: number, w2: number) {
    g.drawPoly(0, 0, [
        x1 - w1, y1,
        x2 - w2, y2,
        x2 + w2, y2,
        x1 + w1, y1,
    ], color);
}

/**
 * 绘制数字
 * @param scoreSprite 
 * @param num 
 * @param x 
 * @param int 
 */
function DrawNumber(g: Graphics, num: number) {
    let numStr = num.toString();
    let len = numStr.length;

    for (let i = 0; i < len; ++i) {
        //找出每个数字在图片里对应的位置
        let index = -1;
        for (let j = 0; j < charItem.length; ++j) {
            if (charItem[j] == numStr[i]) {
                index = j;
                break;
            }
        }
        if (itemTexs[index] != null) {
            g.drawTexture(itemTexs[index], i * itemSize * 0.13, 0, itemSize * 0.13, itemSize * 0.13);
        }
    }
}


function rgbToHex(r, g, b) {
    // 将每个分量转换为 16 进制，并确保是两位
    const toHex = (value) => {
        const hex = value.toString(16);
        return hex.length === 1 ? '0' + hex : hex;
    };

    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

/**
 * 计算分数
 * @param score 
 * @param operatorIndex 
 * @param numberIndex 
 */
function caculateScore(score: number, operatorIndex: number, numberIndex: number) {
    let op = charItem[operatorIndex];
    let num = parseInt(charItem[numberIndex]);
    if (op == '+') {
        return score + num;
    }
    else if (op == '-') {
        return score - num;
    }
    else if (op == '*') {
        return score * num;
    }
    else if (op == '/') {
        return score / num;
    }
    return score % num;
}

function Main() {


    WinWidth = Config.WINDOW_WIDTH;
    WinHeight = Config.WINDOW_HEIGHT;
    roadWidth = 1800;
    roadSegLength = 180;
    roadCount = 1884;//3.14*600
    itemSize = 450;
    charItem = "1234567890+*/-%";
    score = 0;
    itemTexs = [];

    let bg: Texture = null;



    AssetManager.loadImage("asset/image/cloud.png", Callback.New((tex: Texture) => {
        bg = tex;
    }, this));

    AssetManager.loadImage("asset/image/item.png", Callback.New((tex: Texture) => {

        for (let i = 0; i < charItem.length; i++) {
            let left = (i % 5) * itemSize;
            let top = Math.floor(i / 5) * itemSize;

            var tempSprite = new Sprite();
            tempSprite.graphics.fillTexture(tex, 0, 0, itemSize, itemSize, 'no-repeat', new Point(-left, -top));
            var tempTex = AssetManager.drawToTexture(tempSprite, itemSize, itemSize);
            tempSprite.graphics.clear();
            tempSprite.dispose();
            itemTexs[i] = tempTex;

        }

    }, this));


    GameAudio.playBGM("asset/audio/tianfuyue.MP3", 0.5, 999999);


    var canvas = new Sprite();
    stage.addChild(canvas);

    let roads: Array<Road> = [];
    for (let i = 0; i < roadCount; i++) {
        //曲率的设置
        let curve = (i > 0 && i < 300) ? 0.5 : -0.5;
        let road = new Road(0, 0, (i + 1) * roadSegLength, curve);
        roads.push(road);
    }

    //相机X坐标
    let cameraX: number = 0;
    //相机Y坐标
    let cameraY: number = 1600;
    //相机Z坐标
    let cameraZ: number = 0;
    //相机面向Z轴方向的移动速度
    let speed: number = 100;
    let speedX: number = 0;
    //跳跃标志
    let isJumping: boolean = false;
    //y加速度
    let dy: number = 0
    //增加y的量
    let y: number = 0;

    //操作
    stage.on(EventObject.KEY_DOWN, this, (key: EventObject) => {
        //debugger
        if (key.keyCode == 87 || key.keyCode == 119) {
            //w
            speed += 2;
            if (speed > 1000) speed = 1000;
            if (speed == 500) {
                speed = 502;
                GameAudio.playBGM("asset/audio/liumaishenjian.MP3", 0.5, 999999);
            }
        }

        if (key.keyCode == 83 || key.keyCode == 115) {
            //s
            speed -= 2;
            if (speed < 100) speed = 100;
            if (speed == 500) {
                speed = 498;
                GameAudio.playBGM("asset/audio/tianfuyue.MP3", 0.5, 999999);
            }
        }

        if ((key.keyCode == 99 || key.keyCode == 67 || key.keyCode == 122 || key.keyCode == 90) && !isJumping) {
            //空格
            isJumping = true;
            dy = 150;
            GameAudio.playSE("asset/audio/jump.mp3");
        }
    });

    stage.on(EventObject.KEY_DOWN, this, (key: EventObject) => {
        if (key.keyCode == 65 || key.keyCode == 97) {
            //a
            speedX = -100;
        }

        if (key.keyCode == 68 || key.keyCode == 100) {
            //d
            speedX = 100;
        }
    })

    stage.on(EventObject.KEY_UP, this, (key: EventObject) => {
        if (key.keyCode == 65 || key.keyCode == 97) {
            //a
            speedX = 0;
        }

        if (key.keyCode == 68 || key.keyCode == 100) {
            //d
            speedX = 0;
        }
    })



    //update
    os.add_ENTERFRAME(() => {

        cameraZ += speed;
        cameraX += speedX;

        if (isJumping) {
            //跳跃。
            dy -= 5;
            y += dy;
            if (y <= 0) {
                y = 0;
                isJumping = false;
                GameAudio.playSE("asset/audio/falldown.mp3");
            }
        }

        //最大长度
        let totalLength = roadCount * roadSegLength;
        //主要是限制cameraZ在循环内，超出totalLenght，回到原点
        if (cameraZ >= totalLength) cameraZ -= totalLength;
        if (cameraZ < 0) cameraZ += totalLength;

        //计算出现在位置是在哪个road上。因为road是根据间隔roadSegLength创建的
        let roadIndex = Math.floor(cameraZ / roadSegLength);
        let x = 0, dx = 0;
        //相机Y 的位置默认 在当前road的y上。然后再往上加1600的高度，就是俯视
        cameraY = 1600 + roads[roadIndex].y;


        let minY = WinHeight;

        canvas.graphics.clear();

        if (bg != null) {
            canvas.graphics.drawTexture(bg, 0, 0);
        }


        DrawNumber(canvas.graphics, score);


        //绘制从当前到远处300范围内的道路
        for (let i = roadIndex; i < roadIndex + 300; ++i) {
            let now = roads[i % roadCount];
            now.project(cameraX - x, cameraY + y, cameraZ - (i >= roadCount ? totalLength : 0));
            dx += now.curve;
            x += dx;
            if (!i) {
                continue;
            }
            if (now.Y >= WinHeight) {
                //道路超出屏幕以后，即走路路过。当做是装到ITEM然后进行 积分运算处理
                if (!isJumping && now.operatorIndex != -1) {
                    //如果现在不是处于跳跃，当然路段存在有item的情况下
                    score = Math.floor(caculateScore(score, now.operatorIndex, now.numberIndex));
                    GameAudio.playSE("asset/audio/get.mp3");
                    //trace(score)
                    //消除掉
                    now.operatorIndex = -1;
                    //在当前1500处重新生成
                    //trace((i + (Math.random() * 800) + 300) % roadCount);
                    roads[Math.floor(i + (Math.random() * 500) + 500) % roadCount].generateItem(true);
                    //sound.setBuffer(buffer[0]);
                    //sound.play();
                }
            }
            //远处的 y值小于 近处的y值，依然会绘制出来。这是不合理的，因为人类的眼睛看到会有遮挡的效果。是不能穿透障碍物的
            //为了解决这个问题，绘制的时候从近到远绘制的时候，
            //算出最大当前最大的y值，如果后面的y值小于当前最大y值，就不绘制
            //2维坐标，y向下是正方向 所以找的是最小值
            if (now.Y < minY) {
                minY = now.Y;
            }
            else {
                continue;
            }

            let pre = roads[(i - 1) % roadCount];
            const grass = (i / 3) % 2 ? rgbToHex(16, 210, 16) : rgbToHex(0, 199, 0);
            const edge = (i / 3) % 2 ? rgbToHex(0, 0, 0) : rgbToHex(255, 255, 255);
            const road = (i / 3) % 2 ? rgbToHex(105, 105, 105) : rgbToHex(101, 101, 101);


            DrawTrape(canvas.graphics, grass, pre.X, pre.Y, WinWidth * 10, now.X, now.Y, WinWidth * 10);
            DrawTrape(canvas.graphics, edge, pre.X, pre.Y, pre.W * 1.3, now.X, now.Y, now.W * 1.3);
            DrawTrape(canvas.graphics, road, pre.X, pre.Y, pre.W, now.X, now.Y, now.W);

        }

        for (let i = roadIndex + 300; i > roadIndex + 1; --i) {
            roads[i % roadCount].drawItem(canvas.graphics);
        }


    }, this)

}